package ds.factcom.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable;

import ds.factcom.domain.client.Client;
import ds.factcom.domain.invoice.Invoice;
import ds.factcom.domain.payment.Payment;
import ds.factcom.exception.ClientHasInvoicesException;
import ds.factcom.exception.ClientHasNotInvoicesPending;
import ds.factcom.exception.ExistDomainException;
import ds.factcom.exception.NotExistDomainException;
import ds.factcom.repository.ClientRepository;

/**
 * Modelo unico de cliente, maneja a todos los clientes de la empresa.
 * Sin importar que tipo sea.
 * @author Maximiliano Herrera
 *
 */
public class ClientModel extends Observable {
	
	private Client client;
	
	public ClientModel(){}
	
	/**
	 * Busca el cliente por medio del numero de documento y su tipo de documento.
	 * @param documentType
	 * @param documentNumber
	 * @throws NotExistDomainException
	 */
	public void findByDocument(String documentType, String documentNumber) throws NotExistDomainException {
		this.client =  ClientRepository.getInstance().findByDocument(documentType, documentNumber);
	}
	
	/**
	 * Busca el cliente pasado como parametro
	 * @param client
	 * @throws NotExistDomainException devuelve la exception
	 */
	public void findClient(Client client) throws NotExistDomainException {
		this.client =  ClientRepository.getInstance().findByEntity(client);
	}
	
	/**
	 * Inserta el cliente que viene como parametro. 
	 * @param client inserta esta cliente
	 * @throws ExistDomainException si llega a existir el cliente devuelve
	 */
	public void insert(String name, String documentType, String documentNumber, String address) throws ExistDomainException{
		try {
			this.findByDocument(documentType, documentNumber);
			if(this.client != null){
				throw new ExistDomainException("Cliente");
			}
		} catch (NotExistDomainException e) {
			List<Invoice> invoices=new LinkedList<Invoice>();
			List<Payment> payments=new LinkedList<Payment>();
			Client client=new Client(name, documentType, documentNumber, address, invoices, payments);
			ClientRepository.getInstance().insert(client);
			this.setChanged();
			this.notifyObservers(Message.CLIENT_SAVED);
		}
	}
	
	/**
	 * elimina al cliente
	 * @param documentType
	 * @param documentNumber
	 * @throws NotExistDomainException
	 * @throws ClientHasInvoicesException
	 */
	public void delete(String documentType, String documentNumber) throws NotExistDomainException, ClientHasInvoicesException{
		this.findByDocument(documentType, documentNumber);
		if (!this.client.getInvoices().isEmpty()){
			throw new ClientHasInvoicesException();
		}
		ClientRepository.getInstance().delete(this.client);
		this.setChanged();
		this.notifyObservers(Message.CLIENT_DELETED);
	}
	
	/**
	 * modifica al cliente viejo por el nuevo pasado por parametro.
	 * @param newClient
	 * @throws NotExistDomainException
	 */
	//TODO(maxi) este esta de mas
	public void modify(String name, String address){
		this.client.setName(name);
		this.client.setAddress(address);
		this.setChanged();
		this.notifyObservers(Message.CLIENT_MODIFIED);
	}
	
	/**
	 * busca clientes pendientes de facturas.
	 * @return
	 * @throws ClientHasNotInvoicesPending
	 */
	public List<Invoice> findClientWithPendingInvoices() throws ClientHasNotInvoicesPending {
		return invoicesOfClientPending(this.client);
	}
	
	/**
	 * Devuelve las facturas que no estan pagas, si no que estan pendientes.
	 * @param client
	 * @return
	 * @throws ClientHasNotInvoicesPending
	 */
	private List<Invoice> invoicesOfClientPending(Client client) throws ClientHasNotInvoicesPending{
		List<Invoice> invoicesOfClientPending = new ArrayList<Invoice>();
		for (Iterator<Invoice> iterator = client.getInvoices().iterator(); iterator.hasNext();) {
			Invoice invoice = (Invoice) iterator.next();
			if (invoice.getPayment() == null){
				invoicesOfClientPending.add(invoice);
			}
		}
		if (invoicesOfClientPending.size() == 0){
			throw new ClientHasNotInvoicesPending();
		}
		return invoicesOfClientPending;
	}
	
	/**
	 * Buscar clientes deudores
	 * @return
	 */
	public List<String> findClientDebtor() {
		List<String> clientsDebtor = new ArrayList<String>();
		for (Iterator<Client> iterator = ClientRepository.getInstance().getAllEntities().iterator(); iterator.hasNext();) {
			Client client = (Client) iterator.next();
				try {
					List<Invoice> invoicesOfClientPending = invoicesOfClientPending(client);
					clientsDebtor.add(this.formatString(client, invoicesOfClientPending));
				} catch (ClientHasNotInvoicesPending e) {}
		};
		return clientsDebtor;
	}
	
	

	//########################## metodos getters ################################
	
	public Client getClient() {
		return client;
	}
	
	/**
	 * 
	 * @author Javier De Luca, Maximiliano Herrera, Matias Samblancat
	 * 
	 * Pega el tipo de documento, numero de documento, el nombre del cliente, monto total.
	 * Para mostrarlo en la lista de deudores. 
	 * @param client
	 * @param invoicesOfClientPending
	 * @return
	 */
	private String formatString(Client client, List<Invoice> invoicesOfClientPending) {
		Float mountTotal = new Float(0F);
		for (Iterator<Invoice> iterator = invoicesOfClientPending.iterator(); iterator.hasNext();) {
			Invoice invoice = (Invoice) iterator.next();
			mountTotal += invoice.getMountAbsoluty();
		}
		String parse = "-";
		String string = client.getDocument().getDocumentType().getValue()+parse+
			client.getDocument().getNumber()+parse+
			client.getName()+parse+
			mountTotal;
		return string;
	}

	public void setClient(Client client) {
		this.client = client;
	}
	
	public int getNumberOfInvoices(Client client) throws ClientHasNotInvoicesPending{
		return invoicesOfClientPending(client).size();
	}
}
